package sensornetwork;

import snifc.SimulatorIfc;
import sensornetwork.sensor.Sensor;
import sensornetwork.Link;
import snifc.sensor.SensorIfc;
import java.io.IOException;


public class Simulator implements SimulatorIfc {
  
  private int ttlMax;
  private int queueSize;
  private int activeCaptor;
  private int steps;
  private int memorySize;
  private int linkSize;
  private int nbSensor;
  private int nbLinks;
  private int typeNetwork;
  private Sensor [] sensors;
  private Link [] Links;
  private static int ttlDefine;
  private static int nbCaptorDefine;

  public Simulator() {
        steps = 0;
  }
    
	public void reset() {
    this.nbSensor = 10;
    sensors = new Sensor[nbSensor];
    this.ttlMax = ttlDefine;
    this.activeCaptor = nbCaptorDefine;
    this.queueSize = 30;
    this.steps = 10;
    this.memorySize = 30;
    this.nbLinks = 0;
    this.linkSize = 10;
  }
   
	public void createSensors() throws Exception {
    System.out.println(".................Create Sensor...................");
    for(int i=0; i < nbSensor; i++) {
      sensors[i] = new Sensor(i, this.queueSize, this.linkSize, this.memorySize, this.ttlMax);
    }
    System.out.println(nbSensor + " sensors have been created");
	}	
	
	public void linkSensors() throws Exception {
    try{
      switch (this.typeNetwork){
        //Anneau
        case 1: Links = new Link[nbSensor];     
                for (int i = 0; i < nbSensor-1; i++, nbLinks++) {     	                                	
                  Links[nbLinks]=new Link(nbLinks, sensors[i].getPort(), sensors[i+1].getPort());
                  sensors[i].getPort().addLink(Links[nbLinks]);
                  sensors[i+1].getPort().addLink(Links[nbLinks]);
                }
                Links[nbLinks] = new Link(nbLinks, sensors[nbSensor-1].getPort(), sensors[0].getPort());
                sensors[nbSensor-1].getPort().addLink(Links[nbLinks]);
                sensors[0].getPort().addLink(Links[nbLinks]);
        break;
        //Centralise
        case 2: Links = new Link[nbSensor-1];
                for (int i = 1; i < nbSensor; i++, nbLinks++) {                      
                  Links[nbLinks] = new Link(nbLinks, sensors[0].getPort(), sensors[i].getPort());
                  sensors[i].getPort().addLink(Links[nbLinks]);
                  sensors[0].getPort().addLink(Links[nbLinks]);
                }
        break;
        //Maille
        case 3: Links = new Link[(nbSensor*(nbSensor-1))/2];
                for(int i = 0; i < nbSensor; i++) {
                  for(int j = i+1; j < nbSensor; j++, nbLinks++) {
                    Links[nbLinks] = new Link(nbLinks, sensors[i].getPort(), sensors[j%nbSensor].getPort());
                    sensors[i].getPort().addLink(Links[nbLinks]);
                    sensors[j%nbSensor].getPort().addLink(Links[nbLinks]);
                  }
                }
        break;
        //Asymetrique
        case 4: Links = new Link[nbSensor];  
                for (int i = 0; i < 4; i++, nbLinks++) { 
                  Links[nbLinks] = new Link(nbLinks, sensors[i].getPort(), sensors[i+1].getPort());
                  sensors[i].getPort().addLink(Links[nbLinks]);
                  sensors[i+1].getPort().addLink(Links[nbLinks]);
                }              
                Links[nbLinks] = new Link(nbLinks, sensors[4].getPort(), sensors[0].getPort());
                sensors[4].getPort().addLink(Links[nbLinks]);
                sensors[0].getPort().addLink(Links[nbLinks]);
                this.nbLinks++;
                
                int nbInsertLien = nbLinks;
                for(int j = nbInsertLien; j< nbSensor; j++){  
                  Links[nbLinks] = new Link(nbLinks, sensors[nbLinks-5].getPort(), sensors[j].getPort());
                  sensors[nbLinks-5].getPort().addLink(Links[nbLinks]);
                  sensors[j].getPort().addLink(Links[nbLinks]);
                  nbLinks++;
                }
        break;
        
        default :
        break;
      }
    } catch(Exception e){
      throw new Exception("Lien non valide"); 
    }	
	}	
	
	public void runSensors() throws Exception {
    System.out.println("START SIMULATION FOR " + steps + " STEPS");
    for(int i = 0; i < steps; i++) {
      System.out.println("--------------------- STEP " + i + " -----------------------");
      if (activeCaptor > nbSensor || activeCaptor <= 0) {
        throw new Exception("Trop de capteur active ("+activeCaptor+"). Nombre de senseur : "+nbSensor);
      } else {
        for(int j = 0; j < activeCaptor; j++) {
          sensors[j].simulateCapture();
        }
      }
      for(int j = 0; j < nbSensor; j++) {
        sensors[j].activateCaptor();
      }
      for(int j = 0; j < nbSensor; j++) {
        sensors[j].activatePort();
      }
      for(int j = 0; j < nbSensor; j++) {
        sensors[j].activateQueue();
      }
      System.in.read();
    }
	}	

	public void showStat() {
    System.out.println("....................Statistics...................");
    for(int i = 0; i < nbSensor; i++) {
        System.out.println(sensors[i]);
    }
	}
	
	public static void main(String [] arg) {
    try {
      System.out.println("###### Init des params ######");
      System.out.println("Valeur du TTL :");
      int ttl = entierInt();
      if (ttl <= 0) {
        throw new Exception("TTL trop petit : "+ttl);
      } else {
        ttlDefine = ttl;
      }
      System.out.println("Nombre de capteur a activer :");
      nbCaptorDefine = entierInt();
      System.out.println("###### Simulator Start ######");
      System.out.println("----------Topologie dispo-----------");
      System.out.println("1- Simuler un reseau en Anneau");
      System.out.println("2- Simuler un reseau en Centralise");
      System.out.println("3- Simuler un reseau en Maille");
      System.out.println("4- Simuler un reseau en Asymetrique");
      System.out.println("------------------------------------");
      System.out.println("Votre choix : ");
      int choix = entierInt();
      switch (choix) {
        case 1: SimulationAnneau();
        break;
        case 2: SimulationCentralise();
        break;
        case 3: SimulationMaille();
        break;
        case 4: SimulationAsymetrique();
        break;
        default: throw new Exception("Choix non valide");
      }
      System.out.println("Fin du programme...");
      System.in.read(); 
    } catch (Exception e) {
      System.out.println(e.getMessage());
      System.out.println("Erreur. Fin...");
    }
	}

  public static void SimulationAnneau() throws Exception {
    Simulator mySimulator = new Simulator();
    mySimulator.getTypeNetwork(1);
    mySimulator.reset();
    mySimulator.resume();
    mySimulator.createSensors();
    mySimulator.linkSensors();
    mySimulator.runSensors();
    mySimulator.showStat();
  }
  
  public static void SimulationCentralise() throws Exception {
    Simulator mySimulator = new Simulator();
    mySimulator.getTypeNetwork(2);
    mySimulator.reset();
    mySimulator.resume();
    mySimulator.createSensors();
    mySimulator.linkSensors();
    mySimulator.runSensors();
    mySimulator.showStat();
  }
  
  public static void SimulationMaille() throws Exception {
    Simulator mySimulator = new Simulator();
    mySimulator.getTypeNetwork(3);
    mySimulator.reset();
    mySimulator.resume();
    mySimulator.createSensors();
    mySimulator.linkSensors();
    mySimulator.runSensors();
    mySimulator.showStat();
  }
  
  public static void SimulationAsymetrique() throws Exception {
    Simulator mySimulator = new Simulator();
    mySimulator.getTypeNetwork(4);
    mySimulator.reset();
    mySimulator.resume();
    mySimulator.createSensors();
    mySimulator.linkSensors();
    mySimulator.runSensors();
    mySimulator.showStat();
  }
  
  public void getTypeNetwork (int net) {
    typeNetwork = net;
  }
  
  public void resume() {
    System.out.println("Packet TTL : " + ttlMax);
    System.out.println("NbSensors : " + nbSensor);
    System.out.println("NbSensors actif : " + activeCaptor);
    System.out.println("Steps : " + steps);
    switch (this.typeNetwork) {
      case 1: System.out.println("Topology : Anneau");
      break;
      case 2: System.out.println("Topology : Centralise");
      break;
      case 3: System.out.println("Topology : Maille");
      break;
      case 4: System.out.println("Topology : Asymetrique");
      break; 
      default: System.out.println("Topology : non specifie");
      break;
    }
  }
  
  public static String chaine() throws Exception {
    String tmp = "";
    char C='\0';
    try {
      while ((C=(char) System.in.read()) !='\n') {
        if (C != '\r')  tmp = tmp+C;
      }
    } catch (IOException e) {
      throw new Exception("Erreur de frappe");
    }
    return tmp;
  } 
  
  // Cette fonction permet de saisir au clavier une variable entière de type int
	public static int entierInt() throws Exception {
		int x=0;
		try {
			x=Integer.parseInt(chaine());
		} catch (NumberFormatException e) {
      throw new Exception("Format numérique incorrect");
		}	
		return x ;
  }
}
